#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

from common.const import RestoreTypeEnum
from common.logger import Logger
from db2.comm.const import Db2Const
from db2.comm.util.param_util import Db2ParamUtil
from db2.restore.hadr.hadr_db_restore_service import HadrDbRestoreService
from db2.restore.hadr.hadr_restore import HadrRestore
from db2.comm.error_code import Db2ErrCode

LOGGER = Logger().get_logger(filename="db2.log")


class HadrDbRestore(HadrRestore):

    def __init__(self, task_name, pid, job_id, sub_job_id, param_dict):
        super().__init__(task_name, pid, job_id, sub_job_id, param_dict)
        self.service = None
        self.init_service()
        self.restore_type_dict = {}
        self.init_restore_type_dict()

    def init_service(self):
        self.service = HadrDbRestoreService(self.parse_param)

    def init_restore_type_dict(self):
        self.restore_type_dict = {
            RestoreTypeEnum.FULL_RESTORE.value: self.full_restore,
            RestoreTypeEnum.DIFF_RESTORE.value: self.diff_restore,
            RestoreTypeEnum.INCRE_RESTORE.value: self.incr_restore,
            RestoreTypeEnum.LOG_RESTORE.value: self.log_restore
        }

    def full_restore(self):
        LOGGER.info("Start to execute restore(full) database.")
        # 执行恢复操作
        if not self.service.exec_db_full_restore():
            LOGGER.error(f"Failed to execute the restore(full).{self.parse_param.get_log_comm()}")
            return False
        LOGGER.info("Execute db restore(full) successfully.")
        self.service.cp_extracted_logs_after_data_copy_restore_on_standby()
        return True

    def incr_restore(self):
        LOGGER.info("Start to execute restore(incr) database.")
        if not self.service.exec_db_incr_restore():
            LOGGER.error(f"Failed to execute the restore(incr).{self.parse_param.get_log_comm()}")
            return False
        LOGGER.info("Execute db restore(incr) successfully.")
        self.service.cp_extracted_logs_after_data_copy_restore_on_standby()
        return True

    def diff_restore(self):
        LOGGER.info(f"Start to execute restore(diff) database.")
        if not self.service.exec_db_diff_restore():
            LOGGER.error(f"Failed to execute the restore(diff).{self.parse_param.get_log_comm()}")
            return False
        LOGGER.info("Execute db restore(diff) successfully.")
        self.service.cp_extracted_logs_after_data_copy_restore_on_standby()
        return True

    def log_restore(self):
        LOGGER.info(f"Start to execute restore(log) restore.")
        if not self.service.exec_log_restore():
            LOGGER.error(f"Failed to execute the log restore.{self.parse_param.get_log_comm()}")
            return False
        LOGGER.info("Execute db restore(log) successfully.")
        return True

    def exec_rollforward(self):
        LOGGER.info(f"Start to execute db rollforward.")
        if self.restore_type != RestoreTypeEnum.LOG_RESTORE:
            if not self.service.exec_db_rollforward():
                LOGGER.error(f"Failed to execute the database rollforward.{self.parse_param.get_log_comm()}")
                return False
        LOGGER.info("Execute rollforward db successfully.")
        return True

    def check_resource(self):
        check_steps = [
            self.check_db_name,
            self.check_cluster_topology
        ]
        for step in check_steps:
            if not step():
                LOGGER.error(f"Failed to execute db(check) cmd.{self.parse_param.get_log_comm()}")
                return False
        LOGGER.info(f"Execute restore db(check) completed.{self.parse_param.get_log_comm()}")
        return True

    def check_db_name(self):
        db_name = Db2ParamUtil.get_db_name_of_db_copy(self.parse_param.param_dict)
        if db_name != self.service.db_name:
            LOGGER.error(f"Database name doesn't match original_db:{db_name}, target_db: {self.service.db_name}."
                         f"{self.parse_param.get_log_comm()}")
            return False
        LOGGER.info("Check db name succeeded.")
        return True

    def check_cluster_topology(self):
        if self.parse_param.get_target_location() == Db2Const.ORIGINAL:
            return True
        original_nodes_num = len(self.parse_param.get_original_host_nodes())
        target_nodes_num = len(self.parse_param.get_target_host_nodes())
        if original_nodes_num != target_nodes_num:
            LOGGER.error(f"The topology of the cluster is inconsistent, src: {original_nodes_num}, "
                         f"target: {target_nodes_num}.{self.parse_param.get_log_comm()}")
            self.action_result.message = "The topology of the cluster is inconsistent."
            self.action_result.body_err = Db2ErrCode.ERROR_COMMON_INVALID_PARAMETER
            return False
        LOGGER.info("Checking the cluster extendinfo topology succeeded.")
        return True
